﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;

namespace DirectX_Test
{
    public partial class Form1 : Form
    {
        private Microsoft.DirectX.Direct3D.Device device = null;
        private Microsoft.DirectX.DirectInput.Device deviceKeyboard = null;  // Input Device
        private Microsoft.DirectX.DirectInput.Device deviceMouse = null;  // Input Device
        
        private VertexBuffer vBuffer = null;
        private IndexBuffer iBuffer = null;

        private float angle;
        private static CustomVertex.PositionNormalColored[] verts = null;
        private static readonly short[] indices = {
                                                    0,1,
                                                    1,2,
                                                    2,3,
                                                    3,4,
                                                    4,5,
                                                    5,0
                                                  };

        private static readonly float hexSize = 2;
        private bool noZoom = false;
        private bool zoomIn = false;
        private float zoomInc = 0.00005f;
        private Vector3 cameraPos = new Vector3(0, 0, 0);

        private float initialWidth;

        public Form1()
        {
            InitializeComponent();

            //this.Width = 800;
            initialWidth = this.Width;

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
        }

        private System.Threading.AutoResetEvent deviceUpdated;
        private System.Threading.ManualResetEvent appShutdown;
        public void InitializeInputWithThread()
        {
            // Create our keyboard device
            deviceKeyboard = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
            deviceKeyboard.SetCooperativeLevel(this, CooperativeLevelFlags.Background |
            CooperativeLevelFlags.NonExclusive);
            deviceUpdated = new System.Threading.AutoResetEvent(false);
            appShutdown = new System.Threading.ManualResetEvent(false);
            deviceKeyboard.SetEventNotification(deviceUpdated);
            System.Threading.Thread threadLoop = new System.Threading.Thread(
            new System.Threading.ThreadStart(this.ThreadFunction));
            threadLoop.Start();
            deviceKeyboard.Acquire();
        }

        private void ThreadFunction()
        {
            System.Threading.WaitHandle[] handles = { deviceUpdated, appShutdown };
            // Continue running this thread until the app has closed
            while (true)
            {
                int index = System.Threading.WaitHandle.WaitAny(handles);
                if (index == 0)
                {
                    UpdateInputState();
                }
                else if (index == 1)
                {
                    return;
                }
            }
        }

        protected override void OnClosed(EventArgs e)
        {
            if (appShutdown != null)
                appShutdown.Set();
        }

        public void UpdateInputState()
        {
            KeyboardState state = deviceKeyboard.GetCurrentKeyboardState();
            
            // Check for key input.
            if (state[Key.Escape])
                this.Close();    // close application

            if (state[Key.Space])
                noZoom = !noZoom;

            if (noZoom)
            {
                if (state[Key.UpArrow])
                    cameraPos.Y += 1;
                if (state[Key.DownArrow])
                    cameraPos.Y -= 1;
                if (state[Key.LeftArrow])
                    cameraPos.X -= 1;
                if (state[Key.RightArrow])
                    cameraPos.X += 1;
            }
            else
            {
                if (state[Key.UpArrow])
                    zoomIn = true;
                if (state[Key.DownArrow])
                    zoomIn = false;
                if (state[Key.LeftArrow])
                    zoomInc = Math.Max(0.00005f, zoomInc - 0.00005f);
                if (state[Key.RightArrow])
                    zoomInc += 0.00005f;
            }
        }

        /// <summary>
        /// Initializes Keyboard Input
        /// </summary>
        public void InitializeInput()
        {
            deviceKeyboard = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
            deviceKeyboard.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            deviceKeyboard.Acquire();

            deviceMouse = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Mouse);
            //deviceMouse.Properties.AxisModeAbsolute = true;
            deviceMouse.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            deviceMouse.Acquire();
        }

        /// <summary>
        /// We will initialize our graphics device here
        /// </summary>
        public void InitializeGraphics()
        {
            // Set our presentation parameters
            PresentParameters presentParams = new PresentParameters();
            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;

            // Create our device
            device = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

            vBuffer = new VertexBuffer(typeof(CustomVertex.PositionNormalColored), 6, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionNormalColored.Format, Pool.Default);
            vBuffer.Created += new EventHandler(this.OnVertexBufferCreate);
            BuildVerts();
            OnVertexBufferCreate(vBuffer, null);
            
            iBuffer = new IndexBuffer(typeof(short), indices.Length, device, Usage.WriteOnly, Pool.Default);
            iBuffer.Created += new EventHandler(this.OnIndexBufferCreate);
            OnIndexBufferCreate(iBuffer, null);
        }

        private void OnIndexBufferCreate(object sender, EventArgs e)
        {
            IndexBuffer buffer = (IndexBuffer)sender;
            buffer.SetData(indices, 0, LockFlags.None);
        }

        private void OnVertexBufferCreate(object sender, EventArgs e)
        {
            VertexBuffer buffer = (VertexBuffer)sender;
            buffer.SetData(verts, 0, LockFlags.None);
        }

        private static void BuildVerts()
        {
            float h = CalculateH(hexSize);
            float r = CalculateR(hexSize);

            float x = -1;
            float y = 1;

            verts = new CustomVertex.PositionNormalColored[6];
            verts[0].Position = new Vector3(x, y, 1.0f);
            verts[0].Normal = new Vector3(0.0f, 0.0f, -1.0f);
            verts[0].Color = System.Drawing.Color.White.ToArgb();
            verts[1].Position = new Vector3(x - h, y - r, 1.0f);
            verts[1].Normal = new Vector3(0.0f, 0.0f, -1.0f);
            verts[1].Color = System.Drawing.Color.White.ToArgb();
            verts[2].Position = new Vector3(x, y - r - r, 1.0f);
            verts[2].Normal = new Vector3(0.0f, 0.0f, -1.0f);
            verts[2].Color = System.Drawing.Color.White.ToArgb();
            verts[3].Position = new Vector3(x + hexSize, y - r - r, 1.0f);
            verts[3].Normal = new Vector3(0.0f, 0.0f, -1.0f);
            verts[3].Color = System.Drawing.Color.White.ToArgb();
            verts[4].Position = new Vector3(x + hexSize + h, y - r, 1.0f);
            verts[4].Normal = new Vector3(0.0f, 0.0f, -1.0f);
            verts[4].Color = System.Drawing.Color.White.ToArgb();
            verts[5].Position = new Vector3(x + hexSize, y, 1.0f);
            verts[5].Normal = new Vector3(0.0f, 0.0f, -1.0f);
            verts[5].Color = System.Drawing.Color.White.ToArgb();
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            KeyboardState state = deviceKeyboard.GetCurrentKeyboardState();

            // Check for key input.
            if (state[Key.Escape])
                this.Close();    // close application

            if (state[Key.Space])
                noZoom = !noZoom;

            if (noZoom)
            {
                if (state[Key.UpArrow])
                    cameraPos.Y += 1;
                if (state[Key.DownArrow])
                    cameraPos.Y -= 1;
                if (state[Key.LeftArrow])
                    cameraPos.X -= 1;
                if (state[Key.RightArrow])
                    cameraPos.X += 1;
            }
            else
            {
                if (state[Key.UpArrow])
                    zoomIn = true;
                if (state[Key.DownArrow])
                    zoomIn = false;
                if (state[Key.LeftArrow])
                    zoomInc = Math.Max(0.00005f, zoomInc - 0.00005f);
                if (state[Key.RightArrow])
                    zoomInc += 0.00005f;
            }

            base.OnKeyUp(e);
        }

        protected override void OnResizeEnd(EventArgs e)
        {
            InitializeGraphics();

            base.OnResizeEnd(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            // Check the mouse state
            MouseState state = deviceMouse.CurrentMouseState;

            if (state.GetMouseButtons()[0] != 0)
            {
                int x = state.X;
            }

            base.OnMouseDown(e);
        }

        public static double DegreesToRadians(double degrees)
        {
            return degrees * System.Math.PI / 180;
        }

        /// <summary>
        /// Outside triangle side (short)
        /// </summary>
        public static float CalculateH(float side)
        {
            return 0.5f * side;
            //return (float)(System.Math.Sin(DegreesToRadians(30)) * side);
        }

        /// <summary>
        /// Outside triangle side (long)
        /// </summary>
        public static float CalculateR(float side)
        {
            return (float)(System.Math.Cos(DegreesToRadians(30)) * side);
        }

        private void SetupCamera()
        {
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, Math.Max(1.0f, this.Width / this.Height), 1.0f, 1000.0f);
            device.Transform.View = Matrix.LookAtLH(new Vector3(cameraPos.X, cameraPos.Y, -30.0f - angle), new Vector3(cameraPos.X, cameraPos.Y, 0), new Vector3(0, 1, 0));

            device.RenderState.Lighting = false;
            device.RenderState.CullMode = Cull.None;
        }

        private void SetupLights()
        {
            device.Lights[0].Type = LightType.Point;
            device.Lights[0].Position = new Vector3(0, 0, 0);
            device.Lights[0].Diffuse = System.Drawing.Color.White;
            device.Lights[0].Attenuation0 = 0.2f;
            device.Lights[0].Range = 10000.0f;
            device.Lights[0].Update();
            device.Lights[0].Enabled = true;
        }

        private void DrawLine(float yaw, float pitch, float roll, float x, float y, float z, float scale)
        {
            if (!noZoom)
                angle += zoomIn ? -zoomInc : zoomInc;

            device.Transform.World = 
                Matrix.RotationYawPitchRoll(yaw, pitch, roll) * 
                Matrix.Translation(x, y, z) * Matrix.Scaling(scale, scale, 1);
            device.DrawIndexedPrimitives(PrimitiveType.LineStrip, 0, 0, 6, 0, indices.Length - 1);
        }

        private void DrawLine(float x, float y)
        {
            if (!noZoom)
                angle += zoomIn ? -zoomInc : zoomInc;

            device.Transform.World = Matrix.Translation(x, y, 0);
            device.DrawIndexedPrimitives(PrimitiveType.LineStrip, 0, 0, 6, 0, indices.Length - 1);
        }
        
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            device.Clear(ClearFlags.Target, System.Drawing.Color.Black, 1.0f, 0);
            SetupCamera();
            //SetupLights();

            float h = CalculateH(hexSize);
            float r = CalculateR(hexSize);
            float r2 = r * 2;
            float hSize = hexSize + h;

            device.BeginScene();

            device.SetStreamSource(0, vBuffer, 0);
            device.Indices = iBuffer;
            device.VertexFormat = CustomVertex.PositionNormalColored.Format;

            for (int column = 0; column < 70; ++column)
            {
                for (int row = 0; row < 70; ++row)
                {
                    if (column % 2 == 0)
                        DrawLine(hSize * column, -(r2 * row));
                    else
                        DrawLine(hSize * column, -r - (r2 * row));
                }
            }
            
            device.EndScene();
            device.Present();

            this.Invalidate();
        }
    }
}
